package com.heima.wemedia.filter;

import com.alibaba.fastjson.JSON;
import com.heima.wemedia.utils.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Version: V1.0
 */
@Component
@Slf4j
@Order(0)  // 越小越优先执行
public class AuthorizeFilter implements GlobalFilter {

    List<String> urlList = Arrays.asList(
            "/v2/api-docs",
            "/login/in"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //1 判断用户是否登录
        //2 是登陆则放行
//        if (url.contains("/login/in")) {
//            return chain.filter(exchange);
//        }
        String url = request.getURI().getPath();
        for (String urls : urlList){
            if (url.endsWith(urls)) {
                return chain.filter(exchange);
            }
        }

        //3 从 head 获取token
        String token = request.getHeaders().getFirst("token");
        //3.1 无 token 返回提示信息和401 拦截
        if (StringUtils.isBlank(token)) {
            //如果不存在，向客户端返回错误提示信息
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("host", request.getRemoteAddress().getHostName());
            resultMap.put("code", HttpStatus.UNAUTHORIZED.value());
            resultMap.put("errorMessage", "需要登录");

            return writeMessage(exchange, resultMap);
        }

        try {
            //3.2 有 token 校验token合法性， 无效则返回提示信息和401
            Claims claims = AppJwtUtil.getClaimsBody(token);
            //-1 0 -》有效     1 2  -》 无效
            int verifyToken = AppJwtUtil.verifyToken(claims);
            if (verifyToken > 0) {  // 无效  拦截
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("host", request.getRemoteAddress().getHostName());
                resultMap.put("code", HttpStatus.UNAUTHORIZED.value());
                resultMap.put("errorMessage", "登录已过期，请重新登录");

                return writeMessage(exchange, resultMap);
            }


            //4 有效：解析jwt令牌，获取的用户id
            Integer id = claims.get("id", Integer.class);

            //5 ****把用户的id 转发到各个微服务中
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
                httpHeaders.add("userId", String.valueOf(id));
            }).build();
            exchange.mutate().request(serverHttpRequest);

        } catch (Exception e) {
           // e.printStackTrace();
            // 记录日志
            log.error("jwt verifyToken is fail,url:{}", url);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("host", request.getRemoteAddress().getHostName());
            resultMap.put("code", HttpStatus.UNAUTHORIZED.value());
            resultMap.put("errorMessage", "登录失败，请重新登录");

            return writeMessage(exchange, resultMap);
        }

        //6 放行
        return chain.filter(exchange);
    }

    /**
     * 返回提示信息 json 数据状态码 401
     * @param exchange
     * @param resultMap
     * @return
     */
    private Mono<Void> writeMessage(ServerWebExchange exchange, Map<String, Object> resultMap) {
        //获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        //设置状态码
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //response.setStatusCode(HttpStatus.OK);
        //设置返回类型 json
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        //设置返回数据
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONBytes(resultMap));
        //响应数据回浏览器
        return response.writeWith(Flux.just(buffer));
    }
}
